



#ifndef __SOFT_SPI__H__
#define __SOFT_SPI__H__



#include"mcu_.h"

/////////////////////    SPI通信协议                        ///////////////////////////////////

/*
同理，这里的代码也是主机的代码。由主机控制时钟线。
一个主机可以连接多个从机，其中SCLK，MOSI，MISO所有从机共用。
片选线SSx单独连接主机。当主机需要和从机通信时，主机把对应的从机的SSx线拉低。
从机发现自己的SSx线被拉低，则表示主机要和它通信了。
*/

/*
SPI协议要点：
SPI有开始信号，停止信号，相比I2C无应答信号。
开始信号：NSS被拉低的下降沿。当NSS被拉低，即开始传输数据。
停止信号：NSS被拉高的上升沿。当NSS被拉高，停止数据传输。

还有一个和I2C不同的地方：I2C采样是在时钟高电平采样的，而SPI采样是在时钟的跳变沿。
所以SPI的从机设备只能使用硬件数字电路实现这个功能。软件只能模拟主机。
*/

//开始信号和停止信号就不封装成函数了。如果有多个从机，直接控制对应的片选线即可。

/*
极性CPOL和相位CPHA：
1、时钟极性CPOL：是指SPI通讯设备处于空闲状态时，SCK信号线的电平信号(即 SPI通讯开始前、NSS线为高电平时SCK的状态)。

2、时钟相位：指数据的采样的时刻。
当 CPHA=0 时，MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“奇数边沿” 被采样。
当 CPHA=1 时，数据线在 SCK 的“偶数边沿” 采样。
问：为什么要有CPHA =0 和CPHA =1？
答：有些外设在被选中后（CS被拉低）就会把数据放到总线上，让主机区读。这个时候用CPHA=0模式。
而有些在设备发送数据前需要一个SCK时钟来激活外设，外设在接收到第一个时钟后才会把数据放到总线上。这个时候用CPHA=1模式。

一共4种通信模式：
/// 模式0：CPOL=0，CPHA =0  SCK空闲为低电平，数据在SCK的奇数边沿即上升沿被采样(提取数据)
/// 模式1：CPOL=0，CPHA =1  SCK空闲为低电平，数据在SCK的偶数边沿即下降沿被采样(提取数据)
/// 模式2：CPOL=1，CPHA =0  SCK空闲为高电平，数据在SCK的奇数边沿即下降沿被采样(提取数据)
/// 模式3：CPOL=1，CPHA =1  SCK空闲为高电平，数据在SCK的偶数边沿即上升沿被采样(提取数据)
*/

//下面是收发数据的代码。默认先发高位。只要将相应的片选线拉低，即可调用相应的通信函数进行通信。

//gpio* NSS;     //从设备选择信号线（低电平有效）
//gpio* SCLK;    //时钟信号线
//gpio* MOSI;    //主设备输出，从设备输入引脚
//gpio* MISO;    //主设备输入，从设备输出引脚

typedef struct __spi_device___
{
	//int io;
	gpio* scl;
	gpio* mosi;
	gpio* miso;
	gpio* nss;

}SPIdevice;

/// <summary>
/// SPI NSS 引脚设置高电平
/// 软件模拟不需要控制nss引脚，但设备读写的时候需要
/// </summary>
/// <param name="device">SPI 设备</param>
void SPI_NSS_H(SPIdevice* device);
/// <summary>
/// SPI NSS 引脚设置低电平
/// 软件模拟不需要控制nss引脚，但设备读写的时候需要
/// </summary>
/// <param name="device">SPI 设备</param>
void SPI_NSS_L(SPIdevice* device);

/// <summary>
/// SPI scl 引脚设置高电平
/// </summary>
/// <param name="device">SPI 设备</param>
void SPI_SCL_H(SPIdevice* device);
/// <summary>
/// SPI scl 引脚设置低电平
/// </summary>
/// <param name="device">SPI 设备</param>
void SPI_SCL_L(SPIdevice* device);
/// <summary>
/// SPI MOSI 引脚设置高电平
/// </summary>
/// <param name="device">SPI 设备</param>
void SPI_MOSI_H(SPIdevice* device);
/// <summary>
/// SPI MOSI 引脚设置低电平
/// </summary>
/// <param name="device">SPI 设备</param>
void SPI_MOSI_L(SPIdevice* device);
/// <summary>
/// 读取SPI MISO 引脚电平
/// </summary>
/// <param name="device">SPI 设备</param>
/// <returns>0低电平，1高电平</returns>
int SPI_MISO_READ(SPIdevice* device);



/// <summary>
/// 模式0：CPOL=0，CPHA =0  SCK空闲为低电平，数据在SCK的奇数边沿即上升沿被采样(提取数据)
/// </summary>
/// <param name="spi">SPI时序的引脚结构体</param>
/// <param name="d">发送的数据</param>
/// <returns>接收的数据</returns>
unt8 Soft_SPI_RW0_CPOL_0_CPHA_0(SPIdevice* spi, unt8 d);

/// <summary>
/// 模式1：CPOL=0，CPHA =1  SCK空闲为低电平，数据在SCK的偶数边沿即下降沿被采样(提取数据)
/// </summary>
/// <param name="spi">SPI时序的引脚结构体</param>
/// <param name="d">发送的数据</param>
/// <returns>接收的数据</returns>
unt8 Soft_SPI_RW1_CPOL_0_CPHA_1(SPIdevice* spi, unt8 d);

/// <summary>
/// 模式2：CPOL=1，CPHA =0  SCK空闲为高电平，数据在SCK的奇数边沿即下降沿被采样(提取数据)
/// </summary>
/// <param name="spi">SPI时序的引脚结构体</param>
/// <param name="d">发送的数据</param>
/// <returns>接收的数据</returns>
unt8 Soft_SPI_RW2_CPOL_1_CPHA_0(SPIdevice* spi, unt8 d);

/// <summary>
/// 模式3：CPOL=1，CPHA =1  SCK空闲为高电平，数据在SCK的偶数边沿即上升沿被采样(提取数据)
/// </summary>
/// <param name="spi">SPI时序的引脚结构体</param>
/// <param name="d">发送的数据</param>
/// <returns>接收的数据</returns>
unt8 Soft_SPI_RW3_CPOL_1_CPHA_1(SPIdevice* spi, unt8 d);










#endif // !__SOFT_SPI__H__









